# ==========一切皆是对象============
"""class Foo:
    def find(self):
        print("我是绑定对象的方法")"""
# <class '__main__.Foo'>
# print(Foo)
# obj = Foo()
# <__main__.Foo object at 0x000002B831EF1220>
# print(obj)
# obj.find()
# type：检查对象，list、str、tuple、set、dict、int、float
# print(type(obj)) # <class '__main__.Foo'>
# print(str)
# name = str("longzai")
# print(name) # longzai：这个longzai也相当于通过str类实例化的对象
# print(type(name)) # <class str>
# pymysql链接案例
# ======================继承===================
import time

"""
语法：
class 父类:
   pass
class 子类(父类):
   pass
"""
class Parent1:
    xxx = 333 # 类属性
    def run(self): # 类方法
        print("我是父类的方法")
class Sub1(Parent1): # 括号里面写上父类
    xxx = '我是子类的333'
    def run(self):
        print("我是子类的方法")
    pass
# if __name__ == '__main__':
#     # 通过子类创建出一个对象
#     s1 = Sub1()
#     # 通过子类可以调用父类的方法
#     s1.run()
#     # 通过子类可以调用父类的类属性
#     print(s1.xxx)
#     """
#     继承优先级：
#     当父类和子类同时都有重名的类方法/类属性：
#     子类>父类
#     """
# ==================利用继承来解决类和类之间的代码冗余问题============
"""
总结对象的相似之处得到了类
总结类的相似之处得到父类
"""
"""class People:
    school = '不造啊'
    def __init__(self,name,age,sex):
        
        #  初始化函数
        # :param name:名字
        # :param age: 年龄
        # :param sex: 性别
        
        self.name = name
        self.age = age
        self.sex = sex
class Student(People): # 创建了Student学生类，继承People父类
    def play(self):
        print("%s play football" % self.name)
class Teacher(People): # 创建Teacher老师类，继承People父类
    def course(self):
        print("%s course"%self.name)
"""
"""
if __name__ == '__main__':
    # 再使用子类Student初始化对象的时候，就会用到父类的__init__方法
    s1 = Student("好健康",95,"女")
    # 使用s1对象调用子类的方法
    s1.play()
    # 打印s1对象的实例属性
    print(s1.__dict__) # {'name': '好健康', 'age': 95, 'sex': '女'}
    

    #再使用子类Teacher初始化对象的时候，就会用到父类的__init__方法
    t1 = Teacher("小白",18,'男')
    # 使用Teahcer子类自己的类方法：course
    t1.course() 
    # 打印t1对象的实例属性
    print(t1.__dict__) # {"name":"小白","age":18,"sex":"男"}
"""


# ================子类派生的新方法中重用父类功能的方式一========
"""
什么是派生：
指的是子类继承父类的属性和方法，并且派生出自己独有的属性与方法
"""
'''
子类重用父类的功能
在子类派生出的新方法中重用父类功能的方式一:
指名道姓地引用某一个类中的函数
'''
"""
class People:
    school = "不造啊"
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
class Student():
    def __init__(self,name,age,sex,score = 0):
        # self.name = name
        # self.age = age
        # self.sex = sex
        # self.score = score
        People.__init__(self,name,age,sex)
        self.score = score
    def play(self):
        print("%s play football" % self.name)
class Teacher():
    def __init__(self,name,age,sex,hobby):
        People.__init__(self,name,age,sex)
        self.hobby  = hobby
    def course(self):
        print("%s course" %self.name)
"""
"""
if __name__ == '__main__':
    # 通过Student类创建stu1对象
    stu1 = Student("社会琳",96,"男",99)
    # 打印stu1对象的实例属性
    print(stu1.__dict__) # {'name': '社会琳', 'age': 96, 'sex': '男', 'score': 99}

    # 通过Teacher类创建tea1对象
    tea1 = Teacher("小白",19,"男","打篮球")
    # 打印tea1的所有实例属性
    print(tea1.__dict__) # {'name': '小白', 'age': 19, 'sex': '男', 'hobby': '打篮球'}
"""

# ==============在子类派生的新方法中重用父类功能的方式二=========
"""
派生实例化除了父类的属性添加，还能有自己独有的属性 ******
在子类派生出的新方法中重用父类功能的方式二:super()必须在类中用
super(自己的类名,自己的对象)
可以省略传值
super()
"""
"""
class People:
    school = "不造呀"
    def __init__(self,name ,age, sex):
        self.name = name
        self.age = age
        self.sex = sex
class Student(People):# 继承父类
    def __init__(self,name,age,sex,score = 0):
        # super(自己的类名,自己的对象)
        super(Student,self).__init__(name,age,sex)
        self.score = score
    def play(self):
        print("%s play football" % self.name)
class Teacher(People):
    def __init__(self,name,age,sex,hobby):
        super(Teacher, self).__init__(name,age,sex)
        self.hobby = hobby
    def course(self):
        print("%s course"% self.name)
"""
"""
if __name__ == '__main__':
    # 通过Student子类创建stu1对象
    stu1 = Student("好健康",99,"女")
    # 打印stu1对象的所有实例属性
    print(stu1.__dict__) # {'name': '好健康', 'age': 99, 'sex': '女', 'score': 0}

    # 通过Teacher子类创建tea1对象
    tea1 = Teacher("小白",19,"男","打游戏")
    # 打印tea1对象的所有实例属性
    print(tea1.__dict__)  # {'name': '小白', 'age': 19, 'sex': '男', 'hobby': 打游戏}
"""

# ==============在单继承背景下的属性查找====================
"""
class Foo(object): # 实际上有一个默认继承，所有类都会继承它
    # 爷爷
    xxx = 444
    pass
class Bar1(Foo):
     # 爸爸
    # xxx = 333
    pass
class Bar2(Bar1):
    # 爸爸的儿子，爷爷的孙子
    # xxx = 222
    pass
"""
# 孙子 > 爸爸 > 爷爷
"""
分别是有三个类
Foo：看起来没有继承任何的类，但会有一个默认继承关系：object
Bar1：继承Foo类
Bar2：继承Bar1
"""
"""
if __name__ == '__main__':
    # 通过Bar1类创建b1对象
    # b1 = Bar1()
    # print(b1.xxx) # 打印xxx类属性

    # 通过Bar2创建b2对象
    b2 = Bar2()
    print(b2.xxx) # 会是多少？
"""
"""
单继承关系的：
子类没有的会去找父类>父类没有的>父类的父类
"""

#================在多继承背景下的属性查找==========
# 在多继承背景下属性的查找优先级: **
# 此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去
 # 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
# 第四层 # 顶级类是在最后一个分之才走的

"""
class G(object):
    # x = 'G'
    pass
class E(G):
    # x = 'E'
    pass
class F(G):
    # x = 'F'
    pass
# 第二层
class B(E):
    # x= "B"
    pass
class C(F):
    # x='C'
    pass
class D(G):
    x='D'
    pass
# 第一层
class A(B,C,D):
    # x = 'A'
    pass
if __name__ == '__main__':
    # obj = A()
    # print(obj.x)
    print(A.mro()) # 优先使用关系
"""
    # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]

#====================组合====================
"""
什么是组合？
组合指的是某一个对象拥有一个属性，该属性的值是另外一个类的对象
"""
"""
class Foo:
    xxx = 100
class Bar:
    yyy = 200
    def zzz(self):
        print("我是Bar方法")
if __name__ == '__main__':
    # 通过Foo和Bar类创建两个对象
    obj = Foo()
    obj1 = Bar()

    # 给obj对象创建对象属性：obj1
    obj.郑可欣 = obj1 # 此时的oj.attr 等价于 obj1

    # 使用obj1的类属性
    print(obj.郑可欣.yyy)
"""
"""
为什么要用组合？
通过为某一个对象添加属性（属性的值是另外一个类的对象）的方式，可以间接将两个类关联/整合/组合到一起
从而减少类与类之间的代码冗余
"""

# ===============为何要用组合：案例===================
"""
class People:
    school = "不造啊"
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
class Student(People):
    def __init__(self,name,age,sex,score = 0):
        super().__init__(name,age,sex)
        self.scroe = score
class Teacher(People):
    def __init__(self,name,age,sex,course = []):
        super().__init__(name,age,sex)
        self.course = course
    def tell_info1(self):
        for i in self.course:
            i.tell_info()
class Course:
    def __init__(self,c_name,c_price):
        self.c_name = c_name
        self.c_price = c_price
    def tell_info(self):
        print(" | 课程名:%s，价格%s |"%(self.c_name,self.c_price))
"""

"""
if __name__ == '__main__':
    # 通过Course类，创建五个课程对象
    python = Course("Python开发",10000)
    math = Course("高等数学",3000)
    English = Course("英语",4000)
    music = Course("音乐",5000)
    drawing = Course("绘画",5000)

    # 通过Teacher类创建tea1对象
    tea1 = Teacher("小白",19,"男")
    # 把music对象添加到老师对象属性里面
    tea1.course.append(music)
    # 打印下tea1对象下的课程列表
    # print(tea1.course[0]) # <__main__.Course object at 0x0000023ACE8AF820>

    # 通过tea1对象，间接调用了Course类下的tell_info的方法

    tea1.tell_info1() # | 课程名:音乐，价格5000 |
"""
"""
    一个函数可以引用另外一个函数：
    def A():
        print("大家好，我是A函数")
    def B():
        A()
        print("大家好，我是B函数")
"""
"""
组合和继承的区别：
组合和继承的区别，继承是把全部的属性和方法让子类可以调用，
而组合只是部分的属性和方法把2个类关联到一起，有些类的属性
不能全部继承，这就用到了组合,组合的是对象，而继承的是类，
继承是在类定义产生的，它是类之间的关联关系，
而组合是类定义后产生的关系，因为它是对象的关联关系

"""

# ============================多态==================
"""
class Animal:
    def speak(self):
        print("我是动物我会说话")

class People(Animal):
    def speak(self):
        print("say hello")

class Dog(Animal):
    def speak(self):
        print("汪汪汪")

class Pig(Animal):
    def speak(self):
        print("哼哼哼")
"""

"""
if __name__ == '__main__':
    # 通过People类创建p1对象
    p1 = People()

    # 通过Dog类创建d1对象
    d1 = Dog()

    # 通过Pig类创建g1对象
    g1 = Pig()

    # 三个类对象都有共同的speak方法
    # 这种的话，我们就可以封装统一的接口（function）去调用他们
    def speak(obj):
        # 哪个对象传过来，我就让那个对象说话
        obj.speak()
    speak(p1)
    speak(d1)
    speak(g1)

    #多态的作用：方便后期再去新增一些类的时候，可以不改变调用方式，都通过统一的接口来去调用（好管理）
    class Duck:
        def speak(self):
            print("呱呱呱")
    d1 = Duck()
    speak(d1)
"""


# ==========================常用的魔术方法===========
# str
"""
class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __str__(self): # 在对象被打印时自动触发，可以用来定义对象被打印时的输出信息
        # 这种双下划线的，都叫做魔术方法
        return "<name：%s age：%s>" %(self.name,self.age)
    def run(self):
        return self.name
if __name__ == '__main__':
    # 通过People类创建p1对象
    p1 = People("社会琳",96)
    print(p1)
"""

# del
# 在对象被删除先自动触发该方法，可以用来回收对象以外其他相关资源，比如系统资源
"""
class Foo:
    def __init__(self,x,filepath,encoding='utf-8'):
        self.x = x
        self.f = open(filepath,'rt',encoding=encoding)

    def __del__(self):
        print("__del__正在运行")
        self.f.close()
if __name__ == '__main__':
    obj = Foo(1,'student.txt')
    print(obj.f.read())
    time.sleep(3) # 强制等待3s
    print(obj.f.read())
"""

# call
# 在对象被调用时会自动触发该方法
class Foo:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __call__(self,*args,**kwagrs):
        print(self,args,kwagrs)
        # <Foo'__main__' object 000x0x0x0x0x>    (2,2,2,2,"好建康",9999)  {"地址":"辽宁沈阳"}
        #         ↓                                      ↓                       ↓
        #       self                                  args                      kwargs
    def run(self):
        print("run")
if __name__ == '__main__':
    # 通过Foo创建了f1的对象
    f1 = Foo("好健康",99)
    # 对象自己调用自己
    f1(2,2,2,2,"好建康",9999,地址='辽宁沈阳') # ------> 走到了__call__方法



































































